package com.example.demo.shiro.realm;

import com.example.demo.dto.Permission;
import com.example.demo.dto.Role;
import com.example.demo.dto.User;
import com.example.demo.entity.SysUser;
import com.example.demo.jwt.JWTConstant;
import com.example.demo.jwt.JWTToken;
import com.example.demo.jwt.JWTUtil;
import com.example.demo.redis.RedisClient;
import com.example.demo.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @author zhanpengguo
 * @date 2019-07-03 14:03
 */
public class CustomRealm extends AuthorizingRealm {

    Logger logger = LoggerFactory.getLogger(CustomRealm.class);

    @Autowired
    UserService userService;
    @Autowired
    RedisClient redisClient;
    /**
     * 权限认证
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        logger.info("========权限认证===========");
        //使用jwttoken
        String username = JWTUtil.getUsername(principalCollection.toString());
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        User user = userService.findByName(username);
        List<Role> roles = user.getRoles();
        roles.forEach(role -> {
            String roleName = role.getRoleName();
            //添加角色
            simpleAuthorizationInfo.addRole(roleName);
            List<Permission> permissions = role.getPermissions();
            //添加权限
            permissions.forEach(permission -> simpleAuthorizationInfo.addStringPermission(permission.getPermission()));
        });
        return simpleAuthorizationInfo;
    }

    /**
     * 身份认证
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        logger.info("========身份认证===========");
        String token = (String) authenticationToken.getCredentials();
        String username = JWTUtil.getUsername(token);
        if (username == null){
            throw new AccountException("token中username为空");
        }
        //查数据库
        User user = userService.findOneUser(username);
        if (user == null){
            throw new AccountException("用户不存在");
        }
        // 开始认证，要AccessToken认证通过，且Redis中存在RefreshToken，且两个Token时间戳一致
        if (JWTUtil.verify(token)){
            if (redisClient.hasKey(JWTConstant.getRefreshTokenKey(username))){
                String tokenCurrentMillis = JWTUtil.getClaim(token,JWTConstant.CLAIM_CURRENT);
                String refreshCurrentMillis = redisClient.get(JWTConstant.getRefreshTokenKey(username)).toString();
                if (tokenCurrentMillis.equals(refreshCurrentMillis)){
                    return new SimpleAuthenticationInfo(token,token,getName());
                }
            }
            throw new AccountException("token已过期");
        }
        throw new AccountException("无效token");
    }

    /**
     * 大坑，必须重写此方法，不然Shiro会报错
     * 使用JWTToken替代原生token
     * @param token
     * @return
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JWTToken;
    }
}
